/** 
 * @author Riccardo Manfrin [namesurname at gmail dot com]
 * 
 * @brief  KILLNET APP FILE
 * 
 */
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>		/* struct sockaddr_in*/
#include <unistd.h>		/* close*/
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>		/* getpid */
#include <unistd.h>		/* getpid */
#include <linux/if.h>		/* struct ifreq */
#include <netinet/in.h>		/* inet_ntoa */
#include <arpa/inet.h>  	/* inet_addr*/
#include <asm/types.h>		/* linux/netlink.h data types */
#include <linux/netlink.h>	/* NETLINK_ROUTE define */
#include <linux/rtnetlink.h>	/* RTM_GETROUTE */
#include <sys/ioctl.h>
#include "sock.h"
#include "wifi.h"
#include "debug.h"

enum {
	MALFORMED_IP,
	WRONG_SKT_OPTION,
	BCAST_NOT_SUPPORTED,
	BIND_TO_DEV_FAILURE,
	BIND_TO_ADDR_FAILURE,
	NLSOCK_FAILURE,
	NLSEND_FAILURE,
	SOCKET_FAILURE,
	CONNECTION_FAILURE,
	IFACE_NOT_FOUND,
	TX_FAILED
} err_code;

static int sfd = -1;
/** LOCAL ROUTE **/
static unsigned int srcport = 0;
static char *src_ip;
static char *iface;
/** REMOTE ROUTE **/
static unsigned int rmtport = 0;
static char *dst_ip;
static int txm = 0;
static int tr = UDP;                    
static struct sockaddr_in rmtaddr;

static void error(int e);

static void error(int e)
{
	printf("sock.c : ERR_CODE %i\n",(int)e);
	return;
}

void init_data()
{
	dst_ip=NULL;
	src_ip=NULL;
	iface=NULL;
	memset(&rmtaddr,0,sizeof(rmtaddr));
	return;
}

int get_sockfd(){
	return sfd;
}

void set_dst_port(unsigned int p){
	rmtport=p;
	rmtaddr.sin_port = htons(rmtport);
	return;
}

void set_src_port(unsigned int p){
	srcport=p;
	return;
}

void set_iface(char *ifc){
	iface = malloc(IFNAMSIZ);
	memset(iface,0,IFNAMSIZ);
	memcpy(iface, ifc, IFNAMSIZ);
	return;
}

char *get_iface(){
	return iface;
}

void set_skt_txmode(int i){
	txm=i;
	return;
}

void set_transport(int t){
	tr=t;
	return;
}

int get_transport(){
	return tr;
}

void set_src_addr(char *ip){
	src_ip=malloc(strlen(ip)+1);
	memset(src_ip, 0, strlen(ip)+1);
	memcpy(src_ip,ip,strlen(ip)+1);
}

int set_dst_addr(char *ip)
{

	dst_ip=malloc(strlen(ip));
	memset(dst_ip, 0, strlen(ip));
	memcpy(dst_ip, ip, strlen(ip));
	
	rmtaddr.sin_family=AF_INET;
	rmtaddr.sin_addr.s_addr = inet_addr(ip);
	return 0;
}

void get_dst_addr(char *retip)
{
	memcpy(retip, dst_ip, strlen(dst_ip));
	return; 
}

int sock_open(){
	int err=0;
	int sockfd;
	struct sockaddr_in addr;
	/** CREATING SOCKET (UDP/TCP) **/
	if(tr==UDP) sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	else if(tr==TCP) sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd<0) {error(SOCKET_FAILURE);return -1;}
	sfd = sockfd;
 	/** ENABLING BROADCAST OPTION FOR THE SOCKET **/
 	if(txm==BC)
 	{
		int bc=1;
		err = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *) &bc, sizeof(bc));
		if (err<0) {error(BCAST_NOT_SUPPORTED); return -1;}
	}
	/** BINDING SOCKET TO A PHYSICAL DEVICE (NOT TO AN IP ADDRESS) **/
	if(iface!=NULL)
	{
		err = setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, iface, sizeof(iface));
 		if (err<0) {error(BIND_TO_DEV_FAILURE); return -1;}
 	}
 	
 	/** BINDING SOCKET TO A SPECIFIC (OR ANY) IP ADDRESS **/
	memset(&addr, 0, sizeof(struct sockaddr_in));
	addr.sin_family      = AF_INET;
	if(src_ip==NULL)
	{
		addr.sin_addr.s_addr = INADDR_ANY; // Should we use this -> "| INADDR_BROADCAST;"
	}
	else
	{
		addr.sin_addr.s_addr = inet_addr(src_ip); //We bind to the iface IP
	}	
	addr.sin_port        = htons(srcport);
 	err = bind(sockfd, (struct sockaddr *) &addr, sizeof(addr));
 	if(err<0) {error(BIND_TO_ADDR_FAILURE); return -1;}
 	
 	/**
 	 * Method 1 (using syscalls to get kernel routeing table)
 	 * Getting interface name from IP address
 	 * by checking the routing table with a netlink socket. 
 	 * [Currently not working right.. too many entries detected];
 	 */
//  	if(get_iface()==NULL)
//  	{
//  		int nlsockfd;
//  		struct nlmsghdr *nlMsgHdr;
// 		struct rtmsg *rtMsg;
// 		int rtLen, len=0;
// 		struct rtattr *rtAttr;
//  		char buff[8192];
//  		int msgSeq = 0;
//  		unsigned int dstaddr, srcaddr, gwaddr;
//  		char result[IFNAMSIZ];
//  		iface = malloc(IFNAMSIZ);
//  		nlsockfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
//  		if(nlsockfd<0) {error(NLSOCK_FAILURE); return -1;}
//  		memset(buff,0,sizeof(buff));
//  		nlMsgHdr = (struct nlmsghdr *)buff;
//  		
//  		/** Packet creation **/
//  		nlMsgHdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message.
// 		nlMsgHdr->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
// 		nlMsgHdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
// 		nlMsgHdr->nlmsg_seq = msgSeq; // Sequence of the message packet.
// 		nlMsgHdr->nlmsg_pid = getpid(); // PID of process sending the request.
// 		
// 		/** Sending the request **/
// 		dbg_print(CHR, "sock.c: NL SOCKET REQ\n");
//  		err=send(nlsockfd, nlMsgHdr, nlMsgHdr->nlmsg_len, 0);
//  		if(err<0) {error(NLSEND_FAILURE); return -1;}
//  		
//  		/** Getting the reply **/
//  		memset(nlMsgHdr, 0, sizeof(struct nlmsghdr));
// 		do{
//  			len = len + recv(nlsockfd, buff+len, sizeof(buff), 0);
//  			if( ((struct nlmsghdr *)(buff+len))->nlmsg_type == NLMSG_DONE ) break;
//  		} while ((nlMsgHdr->nlmsg_seq != msgSeq) || (nlMsgHdr->nlmsg_pid != getpid()));
//  		dbg_print(CHR, "sock.c: NL SOCKET REPLY\n");
//  		
//  		/** Parsing result to get interface **/
// 		while(NLMSG_OK(nlMsgHdr,len))
// 		{
// 			rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsgHdr);
// 			rtAttr = (struct rtattr *)RTM_RTA(rtMsg);
// 			rtLen = RTM_PAYLOAD(nlMsgHdr);
// 			/** If the route is not for AF_INET or does not 
// 			belong to main routing table then return.**/
// 			while(RTA_OK(rtAttr,rtLen)) 
// 			{
// 				//Check if it's the INET route and if it's the main RT (if not breaks)
// 				if((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN)) break;
// 				switch(rtAttr->rta_type) {
// 					case RTA_OIF:
// 					{
// 						if_indextoname(*(int *)RTA_DATA(rtAttr), iface);
// 						break;
// 					}
// 					case RTA_GATEWAY:
// 					{
// 						gwaddr = *(u_int *)RTA_DATA(rtAttr);
// 						break;
// 					}
// 					case RTA_PREFSRC:
// 					{
// 						srcaddr = *(u_int *)RTA_DATA(rtAttr);
// 						break;
// 					}
// 					break;
// 					case RTA_DST:
// 					{
// 						dstaddr = *(u_int *)RTA_DATA(rtAttr);
// 						break;
// 					}
// 					default:
// 					{
// 						break;
// 					}
// 				}
// 				
// 				if(src_ip!=NULL)
// 				{
// 					int i = 0;
// 					i=inet_addr(src_ip);
// 					//memcpy(taddr,&i,4);
// 					//printf("SRC=%04X - DST=%04X - GW=%04X - SK_ADDR=%04X - IFACE=%s\n",srcaddr,dstaddr,gwaddr,i,iface);
// 					if(iface!=NULL && memcmp(&srcaddr,&i,4)==0)
// 					{
// 						if(memcmp(iface,"lo",2)!=0) //The loopback can have this address too
// 						{
// 							//printf("Iface = %s\n", iface);
// 							memset(result,0,IFNAMSIZ);
// 							memcpy(result,iface,IFNAMSIZ);
// 						}
// 					}
// 				}
// 				
// 				rtAttr = RTA_NEXT(rtAttr,rtLen);
// 			}
// 			nlMsgHdr = NLMSG_NEXT(nlMsgHdr,len);
// 		}
// 		close(nlsockfd);
// 		memset(iface,0,IFNAMSIZ);
// 		memcpy(iface,result,IFNAMSIZ);
// 		printf("Iface = %s\n", iface);
//  	}
 	
	/** 
	 * Method 2 (Using SIOCGIFCONF)
	 * IOCTL to return  a  list of interface (transport layer) addresses. 
	 * This currently means only addresses of the AF_INET (IPv4) family for
	 * compatibility.  The user passes a ifconf structure as argument to the ioctl.
	 * It contains a pointer to an array of ifreq structures in ifcu_req and 
	 * its length in bytes in ifc_len.  The kernel fills the ifreqs with all 
	 * current L3 interface addresses that are running: ifr_name contains 
	 * the interface name (eth0:1 etc.), ifr_addr the address.  The kernel  
	 * returns  with  the  actual length  in ifc_len.  If ifc_len is equal 
	 * to the original length the buffer probably has overflowed and you should 
	 * retry with a bigger buffer to get all addresses.  When no error occurs 
	 * the ioctl returns 0; otherwise -1. Overflow is not an error.
	 * 
	 * struct ifconf
	 * {
	 *   int     ifc_len;                        // size of buffer
	 *   union
	 *   {
         *       char *                  ifcu_buf;
         *       struct  ifreq           *ifcu_req;
         *   } ifc_ifcu;
         * };
         *         
	 * struct ifreq
	 * {
	 * #define IFHWADDRLEN     6
	 * #define IFNAMSIZ        16
	 * 	union
	 * 	{
	 * 		char    ifrn_name[IFNAMSIZ];            // if name, e.g. "en0"
	 * 	} ifr_ifrn;
	 * 
	 * 	union {
	 * 		struct  sockaddr ifru_addr;
	 * 		struct  sockaddr ifru_dstaddr;
	 * 		struct  sockaddr ifru_broadaddr;
	 * 		struct  sockaddr ifru_netmask;
	 * 		struct  sockaddr ifru_hwaddr;
	 * 		short   ifru_flags;
	 * 		int     ifru_ivalue;
	 * 		int     ifru_mtu;
	 * 		struct  ifmap ifru_map;
	 * 		char    ifru_slave[IFNAMSIZ];   // Just fits the size 
	 * 		char    ifru_newname[IFNAMSIZ];
	 * 		char *  ifru_data;
	 * 	} ifr_ifru;
	 * };
         * 
         * NOTE 1:
         * 
         * Thanks to the defines in linux/if.h (sometimes net/if.h) we can shorten some
         * important names in the unions, such as
         * 
         *   ifc_ifcu.ifcu_buf  ---> ifc_buf
         *   ifr_ifrn.ifrn_name ---> ifr_name
         * 
         * therefore, the next 3 different sintaxes mean the same thing:
         * 
         * 	 a) ((struct ifreq *)(ifc.ifc_ifcu.ifcu_buf))->ifr_name
         * 	 b) ((struct ifreq *)(ifc.ifc_buf))->ifr_name
         * 	 c) ifc.ifc_ifcu.ifcu_req[0].ifr_ifrn.ifrn_name
         *
         * NOTE 2:
         * When dealing with SIOCGIFCONF ioctl, we need to alloc the ifc_req before and 
         * ifc_len as well. As we don't know the number of ifaces in advance, we try 
         * until we get'em all. The check to know when all interfaces have been 
         * collected is on the ifc_len returned after the IOCTL is called: if it's 
         * equal to the imposed one, then it means that there was a buffer overflow.
         * If it's less then, we got all of the ifaces.
	 **/
	if(get_iface()==NULL)
	{
		unsigned char *addr;
		int cntr=1;
		unsigned int srcaddr = inet_addr(src_ip);
		struct ifconf ifc;
		ifc.ifc_req = malloc(sizeof(struct ifreq)*cntr);
		do{
			free(ifc.ifc_req);
			ifc.ifc_req = malloc(sizeof(struct ifreq)*cntr);
			ifc.ifc_len = sizeof(struct ifreq)*cntr;
			if(ioctl(sockfd, SIOCGIFCONF, &ifc)<0) error(IFACE_NOT_FOUND);
			cntr++;
		} while(ifc.ifc_len==(cntr-1)*sizeof(struct ifreq));
		do {
			addr=(unsigned char*)ifc.ifc_ifcu.ifcu_req[cntr].ifr_ifru.ifru_addr.sa_data+2;
			if(memcmp(addr, &srcaddr,4)==0){
				iface=malloc(IFNAMSIZ);
				memset(iface,0,IFNAMSIZ);
				memcpy(iface, ifc.ifc_ifcu.ifcu_req[cntr].ifr_ifrn.ifrn_name, IFNAMSIZ);
				if(dbg_isverbose())
				{
					printf("sock.c: %s IFACE FOUND\n", iface);
				}
			}
		} while(cntr--);
		free(ifc.ifc_req);
		
	}
 	
	/** 
	 * CONNECTING TO DESTINATION ADDRESS (IN CASE SOCKET IS TCP)
	 * If the socket sockfd is of type SOCK_DGRAM (UDP) then serv_addr 
	 * is the address to which datagrams are sent by default, and the 
	 * only address from which datagrams are received.  If the socket 
	 * is of type SOCK_STREAM (TCP) or SOCK_SEQPACKET, this call 
	 * attempts to make a connection to the socket that is bound 
	 * to the address specified by serv_addr.
	 */
	 
	 if(rmtport!=0 && dst_ip !=NULL)
	 {
 	 	err = connect(sockfd, (struct sockaddr *) &rmtaddr, sizeof(rmtaddr));
	 	if(err<0) {error(CONNECTION_FAILURE); return -1;}
	 }
 
	 return sockfd;
}

void sock_close(int sockfd){
	close(sockfd);
	rmtport=0;
	free(dst_ip);
	free(src_ip);
	dst_ip = NULL;
	sfd = -1;
	return;
}

int sock_send(int sockfd, unsigned char *data, unsigned int length)
{
	int err = sendto(sockfd, data, length, 0,(struct sockaddr *) &rmtaddr, sizeof(struct sockaddr));
	if (err<0) {error(TX_FAILED);return -1;}
	return 0;
}

